package hk.com.fgoproduction.getdroplets.Service

import android.app.Notification
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Environment
import android.os.IBinder
import android.util.Log
import com.dropbox.core.v2.files.WriteMode
import hk.com.fgoproduction.getdroplets.DropboxAPI.AFile
import hk.com.fgoproduction.getdroplets.Lib.Common.ThisApplication
import hk.com.fgoproduction.getdroplets.Lib.Common.ToolInstance.CheckConnection
import hk.com.fgoproduction.getdroplets.Lib.Common.ToolInstance.ToastInstance
import hk.com.fgoproduction.getdroplets.Lib.IO.SpeedLimiter
import hk.com.fgoproduction.getdroplets.R
import org.jetbrains.anko.toast
import java.io.*

class IOService : Service(), ThisApplication.BgThreadCallback {

    lateinit var obj: AFile
    val notiId = 0xFF
    val sleepTime = 2 /*ms*/
    val maxSpeed = 300 * 1024 * sleepTime / 1000 /*KB/ms*/
    val packetSize = 10 * 1024 //KB
    var dlFlag = 0.toByte()

    override fun onBind(intent: Intent?): IBinder? {
        return null
    }

    override fun onCreate() {
        super.onCreate()

        if (!CheckConnection.isOnline(this)) {
            toast("No connection! Please check again!")
            stopSelf()
        }

        obj = (application as ThisApplication).fileApiFactory(this)
        startForeground(notiId, getNotiBuilder(this).build())
        ToastInstance.show(this, "Start")
    }

    private fun getNotiBuilder(context: Context, title: String = "Test Notification", text: String = "Test Content") : Notification.Builder {
        return Notification.Builder(context)
                .setContentTitle(title)
                .setContentText(text)
                .setSmallIcon(R.drawable.notification_template_icon_bg)
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        dlFlag = intent?.extras?.getByte("dl") as Byte

        if ((application as ThisApplication).filePathQueue.isEmpty()) {
            stopForeground(true)
            stopSelf()
        }

        startIO(dlFlag)


        return START_STICKY
    }

    private fun startIO(isDl: Byte) {
        (application as ThisApplication).runOnBgWithCb(Runnable {
            val temp = (application as ThisApplication).filePathQueue.elementAt(0)
            Log.d("$this", temp)
            val limiter = SpeedLimiter(maxSpeed, sleepTime, packetSize)
            try {
                io(temp, isDl, limiter, obj)
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }, this)
    }

    private fun io(s: String, action: Byte, limiter: SpeedLimiter, obj: AFile) {
        try {
            if (action === 1.toByte()) {
                downloadWithProgress(s, limiter, obj)
            } else {
                uploadWithProgress(s, limiter, obj)
//            obj?.uploadFile(s)
            }
        } catch (e: Exception) {
            throw e
        }
    }

    private fun downloadWithProgress(s: String, limiter: SpeedLimiter, obj: AFile) {
        var out: OutputStream? = null
        var `in`: InputStream? = null

        try {
            `in` = obj.getDownloadStream(s) as InputStream
            val path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
            val file = File(path, obj.getMetadata(s).name)
            val fileSize = obj.getFileSize(s)

            if (!path.exists()) {
                if (!path.mkdirs()) {
                    throw Exception("Cannot create directory")
                }
            } else if (!path.isDirectory) {
                throw Exception("Download path error")
            }

            out = FileOutputStream(file)
            transmit(`in`, out, fileSize, file.name, limiter)
            out.flush()
        } catch (e: Exception) {
            throw e
        } finally {
            out?.close()
            `in`?.close()
            stopForeground(true)
        }
    }

    @Throws(Exception::class)
    private fun transmit(`in`: InputStream, out: OutputStream, fileSize: Long, fileName: String, limiter: SpeedLimiter) {
        val buffer = ByteArray(packetSize)
        var i = 0
        var downloadedSize = 0L
        var progress = 0
//        limiter.start()
        Log.d("Thread Started", Thread.currentThread().id.toString())
        Log.d("File", fileName)

        while (true) {
            i = `in`.read(buffer)
            if (i == -1) break

            out.write(buffer, 0, i)
            downloadedSize += i
            progress = (downloadedSize * 100.0 / fileSize).toInt()

            val content = progress.toString() + "%"
            startForeground(Thread.currentThread().id.toInt(), getNotiBuilder(this, fileName, content).setProgress(100, progress, false).build())

//            limiter.update(object: SpeedLimiter.ISpeedLimiter {
//                override fun stopTheWorld() {
//                    Thread.sleep(limiter.timeDelayed)
//                }
//            })
        }
    }

    private fun uploadWithProgress(localSrc: String, limiter: SpeedLimiter, obj: AFile) {
        var out: OutputStream? = null
        var `in`: InputStream? = null

        val file = File(localSrc)
        val fileSize = file.length()
        val path = "/temp/" + file.name
        val uploader = obj.getUploader(path, WriteMode.OVERWRITE)

        try {
            out = DataOutputStream(obj.getUploadStream(uploader))

            `in` = FileInputStream(file)
            transmit(`in`, out, fileSize, file.name, limiter)
            out.flush()
            obj.uploadFinish(uploader)
        } catch (e: Exception) {
            throw e
        } finally {
            out?.close()
            `in`?.close()
            stopForeground(true)
        }
    }

    override fun threadFinish() {
        (application as ThisApplication).filePathQueue.remove((application as ThisApplication).filePathQueue.elementAt(0))
        if ((application as ThisApplication).filePathQueue.isEmpty()) {
            Log.d("Stop IO", "IO finished")
            stopSelf()
        } else {
            startIO(dlFlag)
        }
    }

}